1
2
3
4
5
6
7
8
9
10
11
12
13 package org.apache.tapestry5.modules;
14
15 import org.apache.tapestry5.SymbolConstants;
16 import org.apache.tapestry5.internal.AssetConstants;
17 import org.apache.tapestry5.internal.InternalConstants;
18 import org.apache.tapestry5.internal.services.*;
19 import org.apache.tapestry5.internal.services.assets.*;
20 import org.apache.tapestry5.internal.services.messages.ClientLocalizationMessageResource;
21 import org.apache.tapestry5.ioc.*;
22 import org.apache.tapestry5.ioc.annotations.*;
23 import org.apache.tapestry5.ioc.services.FactoryDefaults;
24 import org.apache.tapestry5.ioc.services.SymbolProvider;
25 import org.apache.tapestry5.services.*;
26 import org.apache.tapestry5.services.assets.*;
27 import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
28 import org.apache.tapestry5.services.messages.ComponentMessagesSource;
29
30 import java.util.Map;
31
32
33
34
35 @Marker(Core.class)
36 public class AssetsModule
37 {
38 public static void bind(ServiceBinder binder)
39 {
40 binder.bind(AssetFactory.class, ClasspathAssetFactory.class).withSimpleId();
41 binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
42 binder.bind(AssetPathConstructor.class, AssetPathConstructorImpl.class);
43 binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
44 binder.bind(AssetSource.class, AssetSourceImpl.class);
45 binder.bind(StreamableResourceSource.class, StreamableResourceSourceImpl.class);
46 binder.bind(CompressionAnalyzer.class, CompressionAnalyzerImpl.class);
47 binder.bind(ContentTypeAnalyzer.class, ContentTypeAnalyzerImpl.class);
48 binder.bind(ResourceChangeTracker.class, ResourceChangeTrackerImpl.class);
49 binder.bind(ResourceMinimizer.class, MasterResourceMinimizer.class);
50 binder.bind(AssetChecksumGenerator.class, AssetChecksumGeneratorImpl.class);
51 binder.bind(JavaScriptStackAssembler.class, JavaScriptStackAssemblerImpl.class);
52 }
53
54 @Contribute(AssetSource.class)
55 public void configureStandardAssetFactories(MappedConfiguration<String, AssetFactory> configuration,
56 @ContextProvider
57 AssetFactory contextAssetFactory,
58
59 @ClasspathProvider
60 AssetFactory classpathAssetFactory)
61 {
62 configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
63 configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
64 configuration.add(AssetConstants.HTTP, new ExternalUrlAssetFactory(AssetConstants.HTTP));
65 configuration.add(AssetConstants.HTTPS, new ExternalUrlAssetFactory(AssetConstants.HTTPS));
66 configuration.add(AssetConstants.FTP, new ExternalUrlAssetFactory(AssetConstants.FTP));
67 configuration.add(AssetConstants.PROTOCOL_RELATIVE, new ExternalUrlAssetFactory(AssetConstants.PROTOCOL_RELATIVE));
68 }
69
70
71 @Contribute(SymbolProvider.class)
72 @FactoryDefaults
73 public static void setupSymbols(MappedConfiguration<String, Object> configuration)
74 {
75
76
77 configuration.add(SymbolConstants.MINIFICATION_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
78 configuration.add(SymbolConstants.GZIP_COMPRESSION_ENABLED, true);
79 configuration.add(SymbolConstants.COMBINE_SCRIPTS, SymbolConstants.PRODUCTION_MODE_VALUE);
80 configuration.add(SymbolConstants.ASSET_URL_FULL_QUALIFIED, false);
81
82 configuration.add(SymbolConstants.ASSET_PATH_PREFIX, "assets");
83
84 configuration.add(SymbolConstants.BOOTSTRAP_ROOT, "${tapestry.asset.root}/bootstrap");
85
86 configuration.add("tapestry.asset.root", "classpath:META-INF/assets/tapestry5");
87 configuration.add(SymbolConstants.OMIT_EXPIRATION_CACHE_CONTROL_HEADER, "max-age=60,must-revalidate");
88 }
89
90
91
92
93 @Decorate(id = "GZipCompression", serviceInterface = StreamableResourceSource.class)
94 public StreamableResourceSource enableCompression(StreamableResourceSource delegate,
95 @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
96 boolean gzipEnabled, @Symbol(SymbolConstants.MIN_GZIP_SIZE)
97 int compressionCutoff,
98 AssetChecksumGenerator checksumGenerator)
99 {
100 return gzipEnabled
101 ? new SRSCompressingInterceptor(delegate, compressionCutoff, checksumGenerator)
102 : null;
103 }
104
105 @Decorate(id = "CacheCompressed", serviceInterface = StreamableResourceSource.class)
106 @Order("before:GZIpCompression")
107 public StreamableResourceSource enableCompressedCaching(StreamableResourceSource delegate,
108 @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
109 boolean gzipEnabled, ResourceChangeTracker tracker)
110 {
111 return gzipEnabled
112 ? new SRSCompressedCachingInterceptor(delegate, tracker)
113 : null;
114 }
115
116 @Decorate(id = "Cache", serviceInterface = StreamableResourceSource.class)
117 @Order("after:GZipCompression")
118 public StreamableResourceSource enableUncompressedCaching(StreamableResourceSource delegate,
119 ResourceChangeTracker tracker)
120 {
121 return new SRSCachingInterceptor(delegate, tracker);
122 }
123
124
125 @Decorate(id = "Minification", serviceInterface = StreamableResourceSource.class)
126 @Order("after:Cache,TextUTF8")
127 public StreamableResourceSource enableMinification(StreamableResourceSource delegate, ResourceMinimizer minimizer,
128 @Symbol(SymbolConstants.MINIFICATION_ENABLED)
129 boolean enabled)
130 {
131 return enabled
132 ? new SRSMinimizingInterceptor(delegate, minimizer)
133 : null;
134 }
135
136
137
138
139 @Decorate(id = "CSSURLRewrite", serviceInterface = StreamableResourceSource.class)
140 @Order("after:Minification")
141 public StreamableResourceSource enableCSSURLRewriting(StreamableResourceSource delegate,
142 OperationTracker tracker,
143 AssetSource assetSource,
144 AssetChecksumGenerator checksumGenerator,
145 @Symbol(SymbolConstants.STRICT_CSS_URL_REWRITING) boolean strictCssUrlRewriting)
146 {
147 return new CSSURLRewriter(delegate, tracker, assetSource, checksumGenerator, strictCssUrlRewriting);
148 }
149
150 @Decorate(id = "DisableMinificationForStacks", serviceInterface = StreamableResourceSource.class)
151 @Order("before:Minification")
152 public StreamableResourceSource setupDisableMinificationByJavaScriptStack(StreamableResourceSource delegate,
153 @Symbol(SymbolConstants.MINIFICATION_ENABLED)
154 boolean enabled,
155 JavaScriptStackSource javaScriptStackSource,
156 Request request)
157 {
158 return enabled
159 ? new JavaScriptStackMinimizeDisabler(delegate, javaScriptStackSource, request)
160 : null;
161 }
162
163
164
165
166
167
168 @Decorate(id = "TextUTF8", serviceInterface = StreamableResourceSource.class)
169 @Order("after:Cache")
170 public StreamableResourceSource setupTextAssetsAsUTF8(StreamableResourceSource delegate)
171 {
172 return new UTF8ForTextAssets(delegate);
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 @Contribute(ContentTypeAnalyzer.class)
199 public void setupDefaultContentTypeMappings(MappedConfiguration<String, String> configuration)
200 {
201 configuration.add("css", "text/css");
202 configuration.add("js", "text/javascript");
203 configuration.add("gif", "image/gif");
204 configuration.add("jpg", "image/jpeg");
205 configuration.add("jpeg", "image/jpeg");
206 configuration.add("png", "image/png");
207 configuration.add("swf", "application/x-shockwave-flash");
208 configuration.add("svg", "image/svg+xml");
209 configuration.add("woff", "application/font-woff");
210 configuration.add("ttf", "application/x-font-ttf");
211 configuration.add("eot", "application/vnd.ms-fontobject");
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 @Contribute(CompressionAnalyzer.class)
228 public void disableCompressionForImageTypes(MappedConfiguration<String, Boolean> configuration)
229 {
230 configuration.add("image/*", false);
231 configuration.add("image/svg+xml", true);
232 configuration.add("application/x-shockwave-flash", false);
233 configuration.add("application/font-woff", false);
234 configuration.add("application/x-font-ttf", false);
235 configuration.add("application/vnd.ms-fontobject", false);
236 }
237
238 @Marker(ContextProvider.class)
239 public static AssetFactory buildContextAssetFactory(ApplicationGlobals globals,
240 AssetPathConstructor assetPathConstructor,
241 ResponseCompressionAnalyzer compressionAnalyzer,
242 ResourceChangeTracker resourceChangeTracker,
243 StreamableResourceSource streamableResourceSource)
244 {
245 return new ContextAssetFactory(compressionAnalyzer, resourceChangeTracker, streamableResourceSource, assetPathConstructor, globals.getContext());
246 }
247
248 @Contribute(ClasspathAssetAliasManager.class)
249 public static void addApplicationAndTapestryMappings(MappedConfiguration<String, String> configuration,
250
251 @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
252 String appPackage)
253 {
254 configuration.add("tapestry", "org/apache/tapestry5");
255
256 configuration.add("app", toPackagePath(appPackage));
257 }
258
259
260
261
262
263 @Contribute(Dispatcher.class)
264 @AssetRequestDispatcher
265 public static void provideBuiltinAssetDispatchers(MappedConfiguration<String, AssetRequestHandler> configuration,
266
267 @ContextProvider
268 AssetFactory contextAssetFactory,
269
270 @Autobuild
271 StackAssetRequestHandler stackAssetRequestHandler,
272
273 ClasspathAssetAliasManager classpathAssetAliasManager,
274 ResourceStreamer streamer,
275 AssetSource assetSource)
276 {
277 Map<String, String> mappings = classpathAssetAliasManager.getMappings();
278
279 for (String folder : mappings.keySet())
280 {
281 String path = mappings.get(folder);
282
283 configuration.add(folder, new ClasspathAssetRequestHandler(streamer, assetSource, path));
284 }
285
286 configuration.add(RequestConstants.CONTEXT_FOLDER,
287 new ContextAssetRequestHandler(streamer, contextAssetFactory.getRootResource()));
288
289 configuration.add(RequestConstants.STACK_FOLDER, stackAssetRequestHandler);
290
291 }
292
293 @Contribute(ClasspathAssetAliasManager.class)
294 public static void addMappingsForLibraryVirtualFolders(MappedConfiguration<String, String> configuration,
295 ComponentClassResolver resolver)
296 {
297
298
299 Map<String, String> folderToPackageMapping = resolver.getFolderToPackageMapping();
300
301 for (String folder : folderToPackageMapping.keySet())
302 {
303
304 configuration.add(folder, toPackagePath(folderToPackageMapping.get(folder)));
305
306
307
308 String folderSuffix = folder.equals("") ? folder : "/" + folder;
309
310 configuration.add("meta" + folderSuffix, "META-INF/assets" + folderSuffix);
311 }
312 }
313
314 private static String toPackagePath(String packageName)
315 {
316 return packageName.replace('.', '/');
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333 @Contribute(ComponentMessagesSource.class)
334 public static void setupGlobalMessageCatalog(AssetSource assetSource,
335 @Symbol(SymbolConstants.APPLICATION_CATALOG)
336 Resource applicationCatalog, OrderedConfiguration<Resource> configuration)
337 {
338 configuration.add("ClientLocalization", new ClientLocalizationMessageResource());
339 configuration.add("Core", assetSource.resourceForPath("org/apache/tapestry5/core.properties"));
340 configuration.add("AppCatalog", applicationCatalog);
341 }
342
343 @Contribute(Dispatcher.class)
344 @Primary
345 public static void setupAssetDispatch(OrderedConfiguration<Dispatcher> configuration,
346 @AssetRequestDispatcher
347 Dispatcher assetDispatcher)
348 {
349
350
351
352
353
354 configuration.add("Asset", assetDispatcher, "before:ComponentEvent");
355 }
356 }